package com.obs.juc;

import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class 面试题_线程交替输出问题 {
    static char[] a1 = "1234567".toCharArray(), a2 = "ABCDEFG".toCharArray();
    static Thread t1 = null, t2 = null;

    @Test
    public void lockSupportTest() {
        t1 = new Thread(() -> {
            for (char c : a1) {
                System.out.print(c);//t1输出
                LockSupport.unpark(t2); //叫醒t2
                LockSupport.park(); //t1阻塞

            }

        }, "t1");

        t2 = new Thread(() -> {
            for (char c : a2) {
                LockSupport.park(); //t2加锁
                System.out.print(c); //t2输出
                LockSupport.unpark(t1); //叫醒t1

            }

        }, "t2");
        t1.start();
        t2.start();
    }

    /**
     * 此方法虽然可以实现，但线程优先级并不可靠，可能存在线程2先执行的情况
     */
    @Test
    public void synchronizedTest() {
        final Object o = new Object();
        //锁对象会对应一个队列，队列中存放了所有要竞争锁的线程
        new Thread(() -> {
            synchronized (o) {
                for (char c : a1) {
                    System.out.print(c);
                    try {
                        o.notify();//随机一个线程尝试获取锁
                        o.wait(); //释放锁
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

                o.notify();//必须，否则程序无法停止
            }
        }, "t1").start();
        new Thread(() -> {
            synchronized (o) {
                for (char c : a2) {
                    System.out.print(c);
                    try {
                        o.notify();//随机一个线程尝试获取锁
                        o.wait(); //释放锁
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

                o.notify();
            }
        }, "t2").start();
    }

    /**
     * 使用门栓保证线程1一定先执行，解决上面 synchronizedTest方法的线程优先级问题
     */
    @Test
    public void countDownLatchTest() {
        CountDownLatch latch = new CountDownLatch(1);
        final Object o = new Object();
        new Thread(() -> {

            synchronized (o) {
                for (char c : a1) {
                    System.out.print(c);
                    //保证线程1先输出完了，再打开门栓
                    latch.countDown();
                    try {
                        o.notify();//随机一个线程尝试获取锁
                        o.wait(); //释放锁
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

                o.notify();//必须，否则程序无法停止
            }

        }, "t1").start();


        new Thread(() -> {
            try {
                //如果是线程2先执行的时候，那就先加上门栓，意思就是该线程先在门外等待，让其他线程进入
                //也就是线程1先执行，线程1执行时，先打印结果，然后把门栓打开，此时线程2执行。
                //线程2执行是由于使用了synchronized，所以会保证线程间交替执行逻辑
                //所以门栓的主要作用是保证线程优先级
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (o) {
                for (char c : a2) {
                    System.out.print(c);
                    try {
                        o.notify();//随机一个线程尝试获取锁
                        o.wait(); //释放锁
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

                o.notify();//必须，否则程序无法停止
            }

        }, "t2").start();

    }

    @Test
    public void reentrantLockTest() {
        ReentrantLock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        CountDownLatch latch = new CountDownLatch(1);

        new Thread(() -> {

            lock.lock();
            try {
                for (char c : a1) {
                    System.out.print(c);
                    latch.countDown();
                    condition2.signal();
                    condition1.await();
                }

                condition2.signal();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }


        }, "t1").start();
        new Thread(() -> {
            try {
                //如果线程2先执行，则先挂起当前线程，让其他线程执行
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            /*
            1.尝试获取锁
            2.执行业务逻辑
            3.唤醒其他线程
            4.当前线程等待
             */
            lock.lock();
            try {
                for (char c : a2) {
                    System.out.print(c);
                    condition1.signal();
                    condition2.await();
                }

                condition1.signal();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }


        }, "t2").start();


    }


    @Test
    public void transferTest() {

        TransferQueue<Character> queue = new LinkedTransferQueue<>();

        new Thread(() -> {
            try {
                for (char c : a1) {
                    queue.transfer(c);
                    System.out.print(queue.take());

                }
            } catch (InterruptedException e) {
                e.printStackTrace();

            }

        }, "t1").start();

        new Thread(() -> {
            try {
                for (char c : a2) {
                    System.out.print(queue.take());
                    queue.transfer(c);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();

            }

        }, "t2").start();
    }
}
